package wangwenjun.phase3.executor.completablefuture.demo02;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;

/**
 * @author ChangLiang
 * @date 2020/6/26
 */
@Slf4j
public class AppV1Test {

    private static Integer corePoolSize = 4;

    @Test
    @DisplayName("test sequence exec")
    public void test() throws InterruptedException {

        /**
         * 串行执行 并使用main线程一路执行
         */

        EsService esService = new EsService();

        List<String> list = new ArrayList<>();

        log.warn("start");
        long start = System.currentTimeMillis();

        String items = esService.searchQueryItems("xxx");
        String guides = esService.searchQueryGuides("xxx");
        String applications = esService.getApplications("xxx");
        String appoints = esService.getAppointItems("xxx");

        list.add(items);
        list.add(guides);
        list.add(applications);
        list.add(appoints);

        long end = System.currentTimeMillis();

        log.warn("full execution time consumption:{} ms", (end - start));
        log.info("result:{}", list);
    }

    @Test
    @DisplayName("test ")
    public void test2(){

        /**
         * 同样是串行执行 只是执行的时候使用了线程池来执行
         */

        List<String> list = new ArrayList<>();

        log.warn("start");
        long start = System.currentTimeMillis();

        EsService esService = new EsService();

        ThreadFactory threadFactory = new ThreadFactoryBuilder().setNameFormat("my-thread-pool-thread-%d").build();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePoolSize,
                10,
                0L,
                TimeUnit.MILLISECONDS,
                new ArrayBlockingQueue<>(20),
                threadFactory,
                new ThreadPoolExecutor.AbortPolicy()
        );

        // 启动一个线程去执行items
        try {
            CallFutureMethod(esService, "searchQueryItems", new Class<?>[]{String.class}, new
                    Object[]{"changliang"}, threadPoolExecutor, list);
        } catch (Exception e) {
            e.printStackTrace();
        }


        // 启动一个线程去执行guides
        try {
            CallFutureMethod(esService, "searchQueryGuides", new Class<?>[]{String.class}, new
                    Object[]{"changliang"}, threadPoolExecutor, list);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 启动一个线程去执行applications
        try {
            CallFutureMethod(esService, "getApplications", new Class<?>[]{String.class}, new
                    Object[]{"changliang"}, threadPoolExecutor, list);
        } catch (Exception e) {
            e.printStackTrace();
        }

        // 启动一个线程去执行appoints
        try {
            try {
                CallFutureMethod(esService, "getAppointItems", new Class<?>[]{String.class}, new
                        Object[]{"changliang"}, threadPoolExecutor, list);
            } catch (Exception e) {
                e.printStackTrace();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();

        log.warn("full execution time consumption:{} ms", (end - start));
        log.info("result:{}", list);
    }

    private static void CallFutureMethod(final Object target, final String methodName, final Class<?>[]
            parameterTypes, final Object[] params, ThreadPoolExecutor threadPoolExecutor, List<String> list) {
        // 启动一个线程去执行items 如果2秒内没有返回值 则停止线程
        FutureTask<String> futureTask = new FutureTask<>(new Callable<String>() {

            @Override
            public String call() throws Exception {

                String value;

                try {
                    Method method = target.getClass().getDeclaredMethod(methodName, parameterTypes);
                    Object returnValue = method.invoke(target, params);
                    value = returnValue != null ? returnValue.toString() : null;
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                    throw e;
                } catch (SecurityException e) {
                    e.printStackTrace();
                    throw e;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    throw e;
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                    throw e;
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                    throw e;
                }
                return value;
            }
        });
        threadPoolExecutor.execute(futureTask);
        String result = null;

        try {
            /**获取方法返回值 并设定方法执行的时间为2秒*/
            result = futureTask.get(5, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            futureTask.cancel(true);
            System.out.println("方法执行中断");
        } catch (ExecutionException e) {
            futureTask.cancel(true);
            System.out.println("Execution异常");
        } catch (TimeoutException e) {
            futureTask.cancel(true);
            throw new RuntimeException("invoke timeout", e);
        }
        if (result != null) {
            list.add(result);
        }
    }

    @Test
    @DisplayName("test callable")
    public void test3() {

        /**
         * 串行执行 并使用main线程一路执行
         */

        List<String> list = new ArrayList<>();

        log.warn("start");
        long start = System.currentTimeMillis();

        EsService esService = new EsService();

        String keyword = "changliang";

        Callable<String> itemResult = () -> esService.searchQueryItems(keyword);
        Callable<String> guideResult = () -> esService.searchQueryGuides(keyword);
        Callable<String> appResult = () -> esService.getApplications(keyword);
        Callable<String> appointResult = () -> esService.getAppointItems(keyword);

        try {
            list.add(itemResult.call());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            list.add(guideResult.call());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            list.add(appResult.call());
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            list.add(appointResult.call());
        } catch (Exception e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        log.warn("full execution time consumption:{} ms", (end - start));
        log.info("result:{}", list);
    }

    @Test
    @DisplayName("test thread pool Executor")
    public void test4() {

        /**
         * 并行执行
         */

        List<String> list = new ArrayList<>();

        log.warn("start");
        long start = System.currentTimeMillis();

        EsService esService = new EsService();

        String keyword = "changliang";

        Callable<String> itemResult = () -> esService.searchQueryItems(keyword);
        Callable<String> guideResult = () -> esService.searchQueryGuides(keyword);
        Callable<String> appResult = () -> esService.getApplications(keyword);
        Callable<String> appointResult = () -> esService.getAppointItems(keyword);

        // 使用线程池#submit方法确实是并发执行了
        ThreadPoolExecutor executor = SearchRequestThreadPool.getExecutor();
        Future<String> itemFuture = executor.submit(itemResult);
        Future<String> guideFuture = executor.submit(guideResult);
        Future<String> appFuture = executor.submit(appResult);
        Future<String> appointFuture = executor.submit(appointResult);

        // 获取结果仍旧是串行的 原因就在于Future没有提供回调的方法
        try {
            list.add(itemFuture.get(3, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        try {
            list.add(guideFuture.get(3, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        try {
            list.add(appFuture.get(3, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }
        try {
            list.add(appointFuture.get(3, TimeUnit.SECONDS));
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        }


        long end = System.currentTimeMillis();
        log.warn("full execution time consumption:{} ms", (end - start));
        log.info("result:{}", list);
    }

    @Test
    @DisplayName("test thread pool Executor")
    public void test5() {

        /**
         * 并行执行
         */

        List<String> list = new ArrayList<>();

        log.warn("start");
        long start = System.currentTimeMillis();

        EsService esService = new EsService();

        String keyword = "changliang";

        Callable<String> itemResult = () -> esService.searchQueryItems(keyword);
        Callable<String> guideResult = () -> esService.searchQueryGuides(keyword);
        Callable<String> appResult = () -> esService.getApplications(keyword);
        Callable<String> appointResult = () -> esService.getAppointItems(keyword);

        // 使用线程池#submit方法确实是并发执行了
        ThreadPoolExecutor executor = SearchRequestThreadPool.getExecutor();
        Future<String> itemFuture = executor.submit(itemResult);
        Future<String> guideFuture = executor.submit(guideResult);
        Future<String> appFuture = executor.submit(appResult);
        Future<String> appointFuture = executor.submit(appointResult);

        Thread thread01 = new Thread(() -> {
            try {
                list.add(itemFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-1");
        thread01.start();

        Thread thread02 = new Thread(() -> {
            try {
                list.add(guideFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-2");
        thread02.start();

        Thread thread03 = new Thread(() -> {
            try {
                list.add(appFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-3");
        thread03.start();

        Thread thread04 = new Thread(() -> {
            try {
                list.add(appointFuture.get(3, TimeUnit.SECONDS));
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            } catch (TimeoutException e) {
                e.printStackTrace();
            }
        }, "my-thread-04");
        thread04.start();

        try {
            thread01.join();
            thread02.join();
            thread03.join();
            thread04.join();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();
        log.warn("full execution time consumption:{} ms", (end - start));
        log.info("result:{}", list);
    }
}
